import { readdirSync, existsSync, lstatSync } from "fs";
import { basename, join, parse } from "path";
import { IConvertParser, ITableConfig, ITableData, ITableObj, ITableOutput, ITypeParser } from "../inters/InterfaceApi";
import { convertMgr } from "./mgr/ConvertMgr";
import { langMgr } from "./mgr/LanguageMgr";
import { BaseParser } from "./plugin/BaseParser";
import { ConstParser } from "./plugin/ConstParser";
import { LanguageParser } from "./plugin/LanguageParser";
import { XlsxReader } from "./reader/XlsxReader";
import { AllJsonWriter } from "./writer/AllJsonWriter";
import { JsonMergeBranchWriter } from "./writer/JsonMergeBranchWriter";
import { JsonMergeWriter } from "./writer/JsonMergeWriter";
import { TsWriter } from "./writer/TsWriter";
import { FontWriter } from "./writer/FontWriter";
import { ErrLogWriter } from "./writer/ErrLogWriter";
import { PrimaryKeyChecker } from "./plugin/errchk/PrimaryKeyChecker";
import { logout, logWarn } from "../utils/LogUtil";
import { errLogMgr } from "./mgr/ErrLogMgr";
import { codeMgr } from "./mgr/CodeMgr";
import { getAbsolutePath, pathData } from "../utils/PathUtil";
import { mkdirsSync } from "../utils/CpFile";
import { ErrorChecker } from "./plugin/errchk/ErrorChecker";
import { TypeParserMgr } from "./mgr/TypeParserMgr";
import { EnumParser } from "./plugin/EnumParser";
import { CsWriter } from "./writer/CsWriter";
import { JsonParser } from "./plugin/JsonParser";
export class Convert {

    convertCfg: ITableConfig;

    /** 转换后的数据，被各种插件转换后的数据 */
    tableData: { [tableName: string]: ITableData }
    /** 从xlsx转出来的源数据 */
    tableSrc: { [tableName: string]: ITableObj }
    /** 多个平台的列表，可以用来判断不同的平台使用 */
    plats: string[];
    private _convertParser: { [tableName: string]: IConvertParser }

    protected _tablecRootDir: string;

    getCodeMgr() {
        return codeMgr.init;
    }
    /**
     * 转换
     * @param config table.json的配置
     * @param path table.json所在的dirname路径
     */
    convert(config: ITableConfig, path: string) {
        this._tablecRootDir = path;
        // 格式化config配置
        this._initConfig(config, path);

        errLogMgr.init();
        langMgr.init();
        codeMgr.init();
        TypeParserMgr.inst.init();

        let files = readdirSync(path);
        let fileList: string[] = []
        files.forEach(f => {
            if (f.indexOf('.xlsx') > 0 && f.indexOf("~$") < 0) {
                fileList.push(f);
            }
        });
        if (fileList.length == 0) {
            logWarn('不存在xlsx文件')
            return;
        }

        this.tableData = {};
        this.tableSrc = {};
        this._convertParser = {};
        this.plats = [];
        this._runPlusMethod("OnStart");
        this._parser(fileList, path);
    }

    /** 将传入的config,进行一次格式化,例如:将config中的路径都改成绝对路径等 */
    protected _initConfig(config: ITableConfig, path: string) {
        this.convertCfg = config;

        logout('path', path)
        if (config.input) {
            if (config.input.path) {
                // 如果配置单独设置了输入地址，则使用配置中设置的，为空则是当前地址
                path = join(path, config.input.path);
            }
        } else {
            this.convertCfg.input = {
                exportPrefix: 'sh'
            }
        }
        if (this.convertCfg.errorLog) {
            let op = this.convertCfg.errorLog.outputPath;
            this.convertCfg.errorLog.outputPath = !op ? '' : getAbsolutePath(op, path);
        } else {
            this.convertCfg.errorLog = { check: true, outputPath: '' }
        }

        if (this.convertCfg.fontPath) {
            this.convertCfg.fontPath = getAbsolutePath(this.convertCfg.fontPath, path);
        } else {
            this.convertCfg.fontPath = ''
        }

        this.convertCfg.output.forEach(op => {
            let opPath: string[] = []
            if (op.path) {
                if (typeof op.path == 'string') {
                    opPath = [op.path]
                } else {
                    opPath = op.path;
                }
            }
            opPath.forEach((p, i) => {
                opPath[i] = getAbsolutePath(p, path);
            })
            op.path = opPath;
            op.merge && op.merge.forEach(m => {
                m.path = getAbsolutePath(m.path, path);
                if (op.type == "json") {
                    m.name = m.name || "data"
                    m.nameExt = m.nameExt || ".config"
                } else if (op.type == "js") {
                    m.name = m.name || "sdata"
                    m.nameExt = m.nameExt || ".js"
                }
            })
            if (op.type == "ts") {
                op.fileName = op.fileName || "sdata"
            }
            if (op.type == "cs") {
                op.fileName = op.fileName || "SysTableData"
            }
        })
        if (this.convertCfg.plugin) {
            let pluginPath = this.convertCfg.plugin.path
            this.convertCfg.plugin.path = !pluginPath ? "" : getAbsolutePath(this.convertCfg.plugin.path, path);
        }

        this.convertCfg.tsOption = this.convertCfg.tsOption || {
            tablePrefix: "SeRes", typePrefix: "Se", jsonClsReplace: "JsonCls"
        }

        logout('formatted config', this.convertCfg)
    }

    protected _parser(fileList: string[], path: string) {
        // todo 暂且先这么处理。之后要加上过滤
        let exportTableName = this.convertCfg.input.exportPrefix || 'sh';
        let tempPlat = {}

        // let allTables: { src: any, dist: any }[] = []
        let parserList: IConvertParser[] = [];

        for (var i = 0; i < fileList.length; i++) {
            var result = new XlsxReader().read(join(path, fileList[i]));
            for (var j = 0; j < result.length; j++) {
                let tabName: string = result[j].name;
                if (tabName.indexOf(exportTableName) >= 0) {
                    let tableData: ITableObj = {
                        fileName: this._getFileName(fileList[i]),
                        tabName: tabName,
                        data: result[j].data,
                        platName: ""
                    }
                    // 这里没想到好方法，先粗暴的获取到不同的平台再说
                    let tabTemp = tabName.split('_');
                    if (tabTemp.length > 1) {
                        let p = tabTemp[tabTemp.length - 1]
                        tempPlat[p] = 1;
                        tableData.platName = p;
                    }
                    let fileName = this._getRealFileName(tableData.fileName, tableData.platName);
                    this.tableSrc[fileName] = tableData;

                    let parser: IConvertParser;
                    if (fileList[i].indexOf("Language") >= 0) {
                        parser = new LanguageParser().start(tableData);
                    } else if (fileList[i].indexOf("Const") >= 0) {
                        parser = new ConstParser().start(tableData);
                    } else if (fileList[i].indexOf("Enum") >= 0) {
                        parser = new EnumParser().start(tableData);
                    } else if (fileList[i].indexOf("Json") >= 0) {
                        parser = new JsonParser().start(tableData);
                    } else {
                        parser = new BaseParser().start(tableData)
                        // allTables.push(res);
                    }
                    this._convertParser[fileName] = parser;
                    parserList.push(parser);
                }
            }
        }

        parserList.sort((p1, p2) => p2.order - p1.order);//按照优先级高的先处理

        for (let i = 0; i < parserList.length; i++) {
            parserList[i].parseValue();
        }

        // 获得当前表中要生成的平台列表
        for (let key in tempPlat) {
            this.plats.push(key);// 
        }

        this._runPlusMethod("OnComplete");

        // check data vaild
        if (this.convertCfg.errorLog.check) {
            // 判断主键重复
            new PrimaryKeyChecker().start(null)
            new ErrorChecker().start(null);
        }

        langMgr.finish()
        // output data
        for (let i = 0; i < this.convertCfg.output.length; i++) {
            let cfg = this.convertCfg.output[i]
            if (cfg.type == 'json') {
                this._outputJson(cfg, path);
            } else if (cfg.type == 'js') {

            } else if (cfg.type == 'ts') {
                this._outputTs(cfg, path);
            } else if (cfg.type == 'cs') {
                this._outputCs(cfg, path);
            }
        }

        this._outputErrLog(path)
    }

    private _runPlusMethod(method: string) {
        //运行配置文件根目录下的插件
        this._runAutoPlugins(method)
        // 再查询是否有设置某个path目录的插件
        this._chkAndRunPathPlugins(method);
    }

    protected _runAutoPlugins(method: string) {
        let dir = join(this._tablecRootDir, "plugins")
        if (!existsSync(dir)) {
            return;
        }
        this._runDirPlugins(dir, method);
    }
    protected _chkAndRunPathPlugins(method: string) {
        if (this.convertCfg.plugin == null || !existsSync(this.convertCfg.plugin.path)) {
            return;
        }
        this._runDirPlugins(this.convertCfg.plugin.path, method)
    }

    protected _runDefPlugins(method: string) {
        // console.log('def plugins dir:', pathData.root);
        let defPluginDir = join(pathData.root, 'plugins');
        if (!existsSync(defPluginDir)) {
            mkdirsSync(defPluginDir);
        }
        this._runDirPlugins(defPluginDir, method)
    }

    /** 运行某个目录中的插件 */
    protected _runDirPlugins(dir: string, method: string) {
        let temps = readdirSync(dir); temps.forEach(f => {
            let path = join(dir, f);
            let stat = lstatSync(path);
            if (stat.isDirectory() && existsSync(join(path, "main.js"))) {
                this._runPlugin(join(path, "main.js"), method);
            }
        });
    }

    protected _runPlugin(url: string, method: string) {
        let plugin = require(url);
        if (plugin[method] != null) {
            plugin[method](this);
        }
    }

    protected _outputErrLog(path: string) {
        new ErrLogWriter().write(null, { path: join(path) })
    }

    protected _outputTs(cfg: ITableOutput, path: string) {
        new TsWriter().write(cfg, { path: join(path) })
    }
    protected _outputCs(cfg: ITableOutput, path: string) {
        new CsWriter().write(cfg, { path: join(path) })
    }

    protected _outputJson(cfg: ITableOutput, path: string) {
        new FontWriter().write(null, { path: join(path) })
        new AllJsonWriter().write(cfg, { path: join(path) });
        // 判断是否要合并
        if (cfg.merge && cfg.merge.length) {
            let md = cfg.merge[0];
            if (!md) {
                return;
            }
            if (!md.branchSplit || this.plats.length == 0) {
                // 不分平台，所有文件都合在一起
                new JsonMergeWriter().write(cfg, { path: join(path) })
            } else {
                // 分不同平台，将需要的文件合在一起，不重复不同平台的同类型文件
                new JsonMergeBranchWriter().write(cfg, { path: join(path) })
            }
        }
    }

    protected _getFileName(fileName: string) {
        let temp = basename(fileName);
        let list = temp.split('.')
        return list.slice(0, list.length - 1).join('');
    }

    getTableData(tableName: string, platName: string = "") {
        let fileName = this._getRealFileName(tableName, platName);
        return this.tableData[fileName + '.json'];
    }

    addTableData(data: ITableData, tableName: string, platName: string = "") {
        let fileName = this._getRealFileName(tableName, platName);
        this.tableData[fileName + '.json'] = data;
    }

    getParser(tableName: string, platName: string = "") {
        let fileName = this._getRealFileName(tableName, platName);
        return this._convertParser[fileName];
    }

    registTypeParser(name: string, parser: ITypeParser) {
        TypeParserMgr.inst.registParser(name, parser);
    }

    private _getRealFileName(tableName: string, platName: string) {
        let fileName = tableName;
        if (platName) {
            fileName += "_" + platName;
        }
        return fileName;
    }
}


export function convert(config: ITableConfig, path: string) {
    convertMgr.crtConvert = new Convert();
    convertMgr.crtConvert.convert(config, path);
}